<?php
namespace Core\Libs\JabezSingleton;

class JabezSingletonException {

	
}

class JabezSingleton extends \Core\Libs\Extend\Object {

	public static $_arrayInstances = array();
	public static $classSingleton = TRUE;

	final public static function getInstances() {

		return self::$_arrayInstances;
	}

	final public static function &loadInstance($class, $singleton = TRUE, $reset = TRUE, $return = TRUE, $reflection = TRUE, $arrayParams = array()) {

		$totalParams = 0;
		$totalParams =& count(&$arrayParams);

		if(is_array(&$class)) {

			if(empty($class[1])) {

				$idClass = $class[0];

				if($idClass === '\\') {

					$idClass =& substr(&$idClass, 1);
				}
			}

			elseif($class[1]{0} === '\\') {

				$idClass =& substr(&$class[1], 1);
			}

			if($class[0]{0} === '\\') {

				$class =& substr(&$class[0], 1);
			}
		}

		else {

			if($class{0} === '\\') {

				$class =& substr(&$class, 1);
			}

			$idClass =& $class;
		}	

		if($reset !== FALSE) {

			$reset = TRUE;
		}

		if($singleton !== TRUE || SINGLETON !== TRUE) {

			$ref = NULL;
			$instance =& $ref;
			$instanceRef =& $ref;
			$singleton = FALSE;
		}

		else {

			$instance =& self::$_arrayInstances['object'][$idClass];
			$instanceRef =& self::$_arrayInstances['CoreReflectionClass'];
			$arrayTotal =& self::$_arrayInstances['count'][$idClass];
			$classParents =& self::$_arrayInstances['classParents'][$idClass];
		}

		if($instanceRef === NULL || $instance === NULL || !$singleton) {

			if($instanceRef === NULL || !$singleton) {

				if($reflection && $return) {

					$instanceRef = new \ReflectionClass(&$class);

					if(!$singleton || $return === 'reflection') {

						return $instanceRef;
					}
				}

				elseif($reflection) {

					$instanceRef = new \ReflectionClass(&$class);
					return;
				}

				else {

					$instanceRef = new \ReflectionClass(&$class);
				}
			}

			else {

				$goto = TRUE;
				goto getInstanciaRefConstruct;
			}

			setInstanciaRefConstruct:

			if($arrayParams !== TRUE && $totalParams) {

				try {

					if(!$singleton) {

						$class =& new \ReflectionClass(&$class);

						if($class->hasMethod('__construct')) {

							return $class->newInstanceArgs(&$arrayParams);
						}

						else {

							trigger_error('Error: Parametros esperados não passados ao construtor.', E_USER_ERROR);
						}
					}

					else {

						$instance = new \ReflectionClass(&$class);

						if($instance->hasMethod('__construct')) {

							$instance = $instance->newInstanceArgs(&$arrayParams);
							$classParents = self::getClasses($idClass, FALSE);
						}

						else {

							trigger_error('Error: Parametros esperados não passados ao construtor.', E_USER_ERROR);
						}
					}
				}

				catch(\Exception $e) {

					throw \Core\Uses\Exception('Error: Parametros esperados não passados ao construtor. CLASS: ' . $class, E_ERROR, 100, $instanceRef->getFileName(), $instanceRef->getStartLine());
				}
			}

			else {

				try {

					if(!$singleton) {

						return new $class();
					}

					else {

						$instance = new $class();
						$classParents = self::getClasses($idClass, FALSE);
					}
				}

				catch(\Exception $e) {

					throw \Core\Uses\Exception('Error: Parametros esperados não passados ao construtor. CLASS: ' . $class, E_ERROR, 100, $instanceRef->getFileName(), $instanceRef->getStartLine());
				}
			}

			if($singleton) {

				$arrayTotal =& count($instanceRef->getProperties());

				if($idClass !== 'ReflectionClass' && $arrayTotal) {

					$instanceRef->__construct(&$class);
					self::addDefaultProperties(&$idClass);
				}
			}
		}

		else {

			if($instanceRef !== NULL) {

				getInstanciaRefConstruct:

				if($reflection && $return) {

					$instanceRef->__construct(&$class);

					if(!$singleton || $return === 'reflection') {

						return $instanceRef;
					}
				}

				elseif($reflection) {

					$instanceRef->__construct(&$class);
					return;
				}

				if($goto === TRUE && $instance === NULL) {

					$goto = NULL;
					goto setInstanciaRefConstruct;
				}
			}

			else {

				goto setInstanciaRefConstruct;
			}

			if($totalParams) {		

				if($idClass !== 'ReflectionClass' && $arrayTotal && $reset) {

					self::resetDefaultProperties(&$idClass);
				}

				if($arrayParams !== TRUE) {

					call_user_func_array(array(&$instance, '__construct'), &$arrayParams);
				}

				else {

					try {

						$instance->__construct();
					}

					catch(\Exception $e) {

						echo 'Faltou Parametros para Construtor, CLASSE: '.$class;
						exit();
					}						
				}
			}

			else {

				if($idClass !== 'ReflectionClass' && $arrayTotal && $reset) {

					self::resetDefaultProperties(&$idClass);
				}
			}
		}

		if($return || !$singleton) {

			return $instance;
		}
	}

	final public static function &serialize($idClass) {

		if($idClass === NULL) {

			return serialize(&self::$_arrayInstances['object']);
		}

		else {

			return serialize(&self::$_arrayInstances['object'][$idClass]);
		}
	}

	final public static function &serializeAllInstances() {

		return serialize(&self::$_arrayInstances);
	}

	final protected static function &getDefaultProperties($idClass = NULL) {

		if($idClass === NULL) {

			return self::$_arrayInstances['properties'];
		}

		else {

			return self::$_arrayInstances['properties'][$idClass];
		}
	}

	final public static function &getClasses($idClass, $autoLoad = TRUE) {

		return array_keys(class_parents(&self::$_arrayInstances['object'][$idClass], &$autoLoad));
	}

	final protected static function &getRegularExpressionOfNamespace($arrayClasses = array()) {

		$string = '';

		foreach($arrayClasses as $class) {

			$string .= $class.'|';
		}

		$string =& substr_replace(&$string, '', -1);

		return $string;
	}

	final protected static function &addDefaultProperties(&$idClass) {

		$instance =& self::$_arrayInstances['object'][$idClass];
		$ref =& self::$_arrayInstances['CoreReflectionClass'];
		$arrayAdd =& self::$_arrayInstances['properties'][$idClass];
		$arrayClasses = self::$_arrayInstances['classParents'][$idClass];
		$i = 0;

		gotoReflectionSubClass:
		$arrayProps =& $ref->getProperties();

		foreach($arrayProps as $p) {

			$prop =& $ref->getProperty($p->getName());
			$prop->setAccessible(TRUE);
			$v =& $prop->getValue(&$instance);

			if(!is_null(&$v)) {

				($prop->isPrivate()) ? $arrayAdd['private'][$ref->getName()][$p->getName()] = $v : $arrayAdd['object'][$p->getName()] = $v;
			}
		}

		if($arrayClasses) {

			$ref->__construct(&$arrayClasses[$i]);
			unset($arrayClasses[$i]);
			$i++;
			goto gotoReflectionSubClass;
		}

		if(!count(&$arrayAdd)) {

			$arrayAdd = array();
		}

		unset($arrayProps, $arrayClasses, $p, $prop, $v, $instance, $ref, $arrayAdd);
	}

	final protected static function &resetDefaultProperties(&$idClass) {

		$instance =& self::$_arrayInstances['object'][$idClass];
		$ref =& self::$_arrayInstances['CoreReflectionClass'];
		$arrayClasses = self::$_arrayInstances['classParents'][$idClass];
		$arrayPropsObj = self::$_arrayInstances['properties'][$idClass]['object'];
		$arrayPropsPriv = self::$_arrayInstances['properties'][$idClass]['private'];
		$i = 0;

		gotoReflectionSubClass:
		$arrayProps =& $ref->getProperties();

		foreach($arrayProps as $p) {

			$p =& $ref->getProperty($p->getName());
			$p->setAccessible(TRUE);

			try {

				if(!$p->isDefault() && !$p->isStatic()) {

					$p->setValue(&$instance, NULL);
				}
			}

			catch(\Exception $e) {}
		}

		$arrayProps = (array)$arrayPropsObj + (array)$arrayPropsPriv[$ref->getName()];

		foreach($arrayProps as $p => $v) {

			if(!$ref->hasProperty($p)) {

				continue;
			}

			$set =& $ref->getProperty($p);
			$set->setAccessible(TRUE);

			try {

				if(!$set->isStatic()) {

					$set->setValue(&$instance, $v);
				}
			}

			catch(\Exception $e) {}
		}

		if($arrayClasses) {

			$ref->__construct($arrayClasses[$i]);
			unset($arrayClasses[$i]);
			$i++;
			goto gotoReflectionSubClass;
		}

		unset($arrayProps, $arrayPropsObj, $arrayPropsPriv,$arrayClasses, $p, $set, $instance, $ref);
	}
}
?>